<?php
class M_user extends CI_Model {

	/**
	* This array contains the following:
	* username		The username of the user.
	* signup_date	The date when the user was registred.
	* salt			The basic salt of the user.
	* password		The user's encrypted password.
	* 
	* @var array Contains the information about the user
	*/
	var $info = array(); //Array with user information.
	
	function __construct()
	{
		parent::__construct();
	}
	
	/**
	 * Function for encrypting the user password.
	 *
	 * @param string password The unecrypted password of the user.
	 * @return string The encrypted password in form of a 128 character long hash.
	 */
	private function hash_password($password)
	{
		//Fetching the salt unique for every implementation of the system.
		$site_salt = $this->config->item('password_salt');
		
		//Fetching the whole salt for the user.
		$user_salt = $this->generate_salt();
		
		//Creating a basic hash of the password and the salts.
		$hash = hash('sha512', sha1($user_salt . $password . $site_salt) . hash('whirlpool', $site_salt . $password . $user_salt) . md5($user_salt . $password . $site_salt));
		
		//Reencrypt the hash several times to provide higher security.
		for ($i = 0; $i < 10000; $i++)
			$hash = hash('sha512', sha1($hash) . hash('whirlpool', $hash) . md5($hash));
		
		//Returning the encrypted password.
		return $hash;
	}
	
	/**
	 * Function for generating the salt for the user that are to be used in the password encryption algorithm.
	 * This salt contains username, signup date and the basic user salt.
	 *
	 * @param void
	 * @return string The salt for the user.
	 */
	private function generate_salt()
	{
		return $this->info['salt'] . $this->info['signup_date'] . $this->info['username'];
	}
	
	/**
	 * Function for checking if a username already exists in the database.
	 *
	 * @param string username The username which are to be checked.
	 * @return bool TRUE if the username already exists, False if the username do NOT exist.
	 */
	private function user_exists($username)
	{
		//Fetching all rows with the username to check if it exist.
		$query = $this->db->get_where('users', array('username' => $username));
		
		//Check that there are not too many or too few usernames.
		if ($query->num_rows() > 1) //If there are two users with the same username something is realy wrong.
			die("Something went terrible wrong! Error 0001.");
		elseif ($query->num_rows() < 1)
			return FALSE; //Return that there are NO such user.
		else //Means num_rows == 1.
			return TRUE; //Return that there ARE such an user.
	}
	
	/**
	 * Function that gets the user id from the user name.
	 *
	 * @param string username The username which's id are to be fetched.
	 * @return int user_id The id of the user with the sent username.
	 */
	private function get_id($username)
	{
		//Create the database query.
		$this->db->select('id');
		$this->db->from('users');
		$this->db->where('username', $username);
		
		//Fetching data about the user.
		$query = $this->db->get();
		$result = $query->result_array();
		
		//Returning the user id of selected user name.
		return $result[0]['id'];
	}
	
	/**
	 * Function for loading the information about the user from the database into the user object.
	 *
	 * @param string username The username of the user who's information are to be loaded.
	 * @return void
	 */
	function load_userdata($user_id)
	{
		//Create the database query.
		$this->db->select('id, username, password, salt, signup_date, email, birthday, gender');
		$this->db->from('users');
		$this->db->where('id', $user_id);
		
		//Fetching data about the user.
		$query = $this->db->get();
		
		//Making sure that there actually exist a challenge with the sent id.
		if ($query->num_rows() != 1) {
			die("Something went terrible wrong! Error 0005.");
		}
		
		//Loading data into the user object.
		$result = $query->result_array();
		$this->info = $result[0];
	}
	
	/**
	 * Function for returning information about the user.
	 *
	 * @param array parameters An array containing the names of the element to be returned from the user object variable.
	 * @return mixed Requested information about the user.
	 */
	function get_info($parameters)
	{
		//If no parameters was sent, then return false and abort.
		if (count($parameters) == 0) {
			return FALSE;
		}
		
		//Return false if the info array is empty (the object isn't loaded).
		if (empty($this->info)) {
			return FALSE;
		}
		
		//Creating an array for the return values.
		$return = array();
		
		//Going thru the parameters to see what to return.
		foreach ($parameters as $p)
		{
			//If the parameter sent correspond to a value in the object variabel, then that value is loaded into the return array.
			switch ($p)
			{
				case 'all_info':
					return $this->info;
					break;
				case 'id':
					$return['id'] = $this->info['id'];				
					break;
				case 'username':
					$return['username'] = $this->info['username'];
					break;
				case 'signup_date':
					$return['signup_date'] = $this->info['signup_date'];
					break;
				case 'salt':
					$return['salt'] = $this->info['salt'];
					break;
				case 'password':
					$return['password'] = $this->info['password'];
					break;
				case 'email':
					$return['email'] = $this->info['email'];
					break;
				case 'birthday':
					$return['birthday'] = $this->info['birthday'];
					break;
				case 'gender':
					$return['gender'] = $this->info['gender'];
					break;
				default:
					return FALSE; //If an incorrect index is sent, then return false and abort.
					break;
			}
		}
		
		//If only one parameter is sent, then return only that value.
		if (count($return) == 1) {
			return $return[$parameters[0]];
		}
		
		//Returning values asked for.
		return $return;
	}
	
	/**
	 * Function that returns information about all users.
	 *
	 * @param array parameters An array containing the names of the element to be returned.
	 * @return array return An array containing the requested information about the users.
	 */
	function get_all($parameters)
	{
		//Create the database query.
			//Creating a string variabel that are to contain selected elements in a form that can be used in CodeIgniter function.
			$select = '';
			
			//Putting all selected elements into the string.
			foreach ($parameters as $p) {
				$select .= "$p, ";
			}
			
			//Trimming the string so it can be used in CodeIgniter function.
			trim($select, ", ");
			
			//Creating the query.
			$this->db->select($select);
			$this->db->from('users');
		//Stop creating the database query.
		
		//Fetching data about the user.
		$query = $this->db->get();
		
		//Return the result array containing the user information.
		return $query->result_array();
	}
	
	/**
	 * Function for creating a user.
	 *
	 * @param array parameters Array containing the information about the user that are to be created.
	 * @return bool TRUE if the user was created, else FALSE.
	 */
	function create($parameters)
	{
		//Loading parameters into variabels.
		$username	= $parameters['username'];
		$password	= $parameters['password'];
		$birthday	= $parameters['birthday'];
		$gender		= $parameters['gender'];
		$email		= $parameters['email'];
		
		//Check that the username isn't already taken!
		if ($this->user_exists($username))
			return FALSE;
		
		//Loading userdata into the data array of the user object.
		$this->info['username']		= $username;
		$this->info['signup_date']	= date('Y-m-d H:i:s');
		$this->info['salt']			= sha1(mt_rand());
		$this->info['password']		= $this->hash_password($password);
		$this->info['birthday']		= $birthday;
		$this->info['gender']		= $gender;
		$this->info['email']		= $email;
		
		//Inserting the new user into the database.
		$this->db->insert('users', $this->info);
		
		//Loading the user id into the data array of the user object.
		$this->info['id'] = $this->db->insert_id();
		
		//Returning that the creation was successful.
		return TRUE;
	}
	
	/**
	 * Function for changing the password of an user.
	 *
	 * @param string new_password The new password for the user.
	 * @return void
	 */
	function change_password($new_password)
	{
		//Encrypting the new password.
		$new_password = $this->hash_password($new_password);
		
		//Getting the username.
		$username = $this->info['username'];
		
		//Creating an array with the user information that are to be updated.
		$data = array('password' => $new_password);
		
		//Updating the user password.
		$this->db->where('username', $username);
		$this->db->update('users', $data);
	}
	
	/**
	 * Function for logging in a user.
	 *
	 * @param string username The username entered by the user.
	 * @param string password Unencrypted password entered by the user.
	 * @return bool TRUE if the user was loged in to the system, else FALSE.
	 */
	function login($username, $password)
	{
		//Check if the username even exists.
		if (!$this->user_exists($username))
			return FALSE;
		
		//Fetching user id.
		$user_id = $this->get_id($username);
		
		//Loading user data into the user objekt.
		$this->load_userdata($user_id);
		
		//Encrypting the entered password.
		$password = $this->hash_password($password);
		
		//Checking if the password is exactly the same as the password in the database.
		if ($this->info['password'] === $password)
		{
			//Do logging in.
			$this->session->set_userdata('user_loged_in', TRUE);
			$this->session->set_userdata('user_userid', $this->info['id']);
			
			//Return that the login was successful.
			return TRUE;
		}
		else
			return FALSE; //Return that the login wasn't successful.
	}
	
	/**
	 * Function for logging out a user.
	 *
	 * @param void
	 * @return void
	 */
	function logout()
	{
		$this->session->unset_userdata('user_loged_in');
		$this->session->unset_userdata('user_userid');
	}
}